/****************************************************************************
 *
 *  LinuxChatty
 *  Copyright (C) 2010, David Hsu.
 * 
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 ***************************************************************************/
#ifndef _THREADSERVICE_H_
#define _THREADSERVICE_H_

#include "xml/tinyxml.h"
#include "HTTPClient.h"
#include <string>
#include <vector>
#include <map>

/**
 * Thread structure that holds the information about the current story post
 */
struct StoryPost
{
	unsigned int storyId;
	std::string storyName;
	unsigned int page;
	unsigned int lastPage;
};

/**
 * Holds all the comment information on the post.
 */
struct CommentData
{
	unsigned int replyCount;
	unsigned int lastReplyId;
	std::string date;
	std::string category;
	std::string author;
	unsigned int id;
	std::string preview;
	std::string body;
	
	// Depth is meant to keep track of how far in the reply chain this comment
	// is.  This will signal the display to indent this properly.
	unsigned int depth;
};

/**
 * ThreadService
 *
 * Handles parsing the thread xml data into a formatted structure that can
 * be displayed on the screen. Can also parse threads into a more printable form.
 */
class ThreadService
{
public:
	~ThreadService();
	
	// Reset the thread service and clears any data
	void Reset();
	
	// Accessor function for the current story/thread id
	void SetStoryId(unsigned int id);
	unsigned int GetStoryId() const;
	void SetThreadId(unsigned int id);
	unsigned int GetThreadId() const;
	
	// Track the current comment selected
	void SetCurrentCommentIndex(unsigned int index);
	
	// Set the API server to use
	void SetAPIServer(const std::string& url);
	
	// Refreshes the comment information given the story id and page number
	bool GetStory(unsigned int id, unsigned int pageNumber = 0);
	bool GetCurrentStory(unsigned int pageNumber = 0);
	
	// Post to a thread
	bool PostData(unsigned int threadId, const std::string& text,
	    std::string& error);
	
	// Manually sets the authorization string from a file
	void SetAuthorization(const std::string& authorization);
	const std::string& GetAuthorization() const;
	
	// Creates and decodes the authorization
	const std::string EncodeAuthorization(const std::string& username,
	    const std::string& password);
	bool DecodeAuthorization(const std::string& authorization, 
	    std::string& username, std::string& password);
	
	// Refreshes a specific thread given the id
	bool GetThread(unsigned int id);
	bool GetCurrentThread();
	
	// For debugging purposes.
	bool DebugParseXML(const char * file, bool isStory);
	
	// Accessor functions
	const StoryPost& GetStoryPost();
	const std::vector<CommentData *>& GetRootComments();
	const std::vector<CommentData *>& GetCurrentComments();
	const CommentData * GetCurrentComment();
	const CommentData * GetCurrentComment(unsigned int index);
	unsigned int GetThreadReplies(unsigned int id);
	unsigned int GetLastThreadReplies(unsigned int id);
	
	// Accessor functions for errors
	const HTTPClient::Error GetRecentError();
	const std::string GetRecentTextError();
	
private:
	// Private helper to clear out the vector of CommentData without
	// creating memory leaks
	void ClearCommentData();
	void ClearRootCommentData();
	void ClearAllCommentData();
	
	// Helper function that goes through the XML and parses the information to
	// class data
	bool ParseXML(TiXmlDocument& doc);
	
	// As the comments are the same thing but recurse between comments/comment,
	// this helper recursive function will parse the comment tags
	bool ParseComments(unsigned int depth, TiXmlElement * root);
	
	// Flag for determining whether we're storing the XML data in root comments
	// or current comments. This way, the story threads can be cached while
	// viewing a specific thread and not have to reload the story when going
	// back to the root.
	bool isLoadingStory;
	
	// Makes the posted information XML friendly so it can be posted to the
	// thread
	std::string MakePostToXML(const std::string& postData);
	
	// Current story that this thread is on
	StoryPost storyPost;
	
	// Caches the current root story threads for quick viewing
	std::vector<CommentData *> rootComments;
	
	// Keeps track of all the comments to the post.
	// Making this a pointer since a thread can potentially have over
	// 1000 comments, which will take up a lot of memory.
	std::vector<CommentData *> currentComments;
	
	// Tracks all the previously read thread post IDs and stores
	// the number of replies to them.  This will be used to show how many
	// new replies have been made since this thread was last seen.
	std::map<unsigned int, unsigned int> threadReplies;
	
	// Map of the last thread replies, which can be used to compare against
	// the total thread replies to provide how many new comments were made
	std::map<unsigned int, unsigned int> lastThreadReplies;
	
	// Authorization string to allow for posting. Either from an XML file or
	// setting up the username/password in the setting screen
	std::string authorizationString;
	
	// HTTP class that will retrieve information from the chatty
	HTTPClient client;
	
	// Keep track of the current story and thread id that will be loaded
	unsigned int storyId;
	unsigned int threadId;
	
	// Keep track of the current selected comment, if any
	unsigned int currentCommentIndex;
};

#endif
